package com.juku2024.juku.analysis.source

import android.content.Context
import android.net.Uri
import android.text.TextUtils
import android.util.Base64
import com.juku2024.juku.analysis.BaseHttpParse
import com.juku2024.juku.analysis.OnParseVideoListener
import com.google.gson.Gson
import com.juku2024.juku.analysis.model.FLVCDModel
import com.juku2024.juku.db.entity.SourceEntity
import com.juku2024.juku.network.api.AnalysisApi
import com.juku2024.juku.network.retrofit.RetrofitUtils
import com.juku2024.juku.tools.LogC
import com.juku2024.juku.tools.RxUtils
import com.juku2024.juku.tools.SPUtil
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.json.JSONObject

class FlvcdHttpParse: BaseHttpParse() {

    override fun parseVideo(context: Context, url: String, source: SourceEntity, callback: OnParseVideoListener) {
        super.parseVideo(context, url, source, callback)
        val targetUrl = source.url + url
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java).parseString(targetUrl)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ firstParseResultJson ->
                if (firstParseResultJson == null) {
                    LogC.e("parse -> ${source.sourceName} 第一次请求，返回结果==null")
                    callback.onFail(source.sourceName)
                    return@subscribe
                }
                val jsonObject = JSONObject(firstParseResultJson)
                val headerC = HashMap<String, String>()
                if (jsonObject.has("HEADER_C")) {
                    val headerJsonObject = jsonObject.getJSONObject("HEADER_C")
                    headerJsonObject.keys().forEach {
                        if (it != null) {
                            headerC[it] = Base64.decode(headerJsonObject.optString(it), Base64.NO_WRAP).toString()
                        }
                    }
                }

                val header = HashMap<String, String>()
                if (jsonObject.has("HEADER")) {
                    val headerJsonObject = jsonObject.getJSONObject("HEADER")
                    headerJsonObject.keys().forEach {
                        if (it != null) {
                            header[it] =Base64.decode(headerJsonObject.optString(it), Base64.NO_WRAP).toString()
                        }
                    }
                }
                val firstParseResult = Gson().fromJson(firstParseResultJson, FLVCDModel::class.java)
                if (firstParseResult == null) {
                    callback.onFail(source.sourceName)
                    return@subscribe
                }

                if (firstParseResult.error == "1") {
                    callback.onFail(source.sourceName)
                    return@subscribe
                }

                if (firstParseResult.type != "CUSTOM") {
                    LogC.e("parse -> ${source.sourceName} 直接返回，无需二次运算")
                    val realUrl = firstParseResult.v?.firstOrNull()?.u
                    if (TextUtils.isEmpty(realUrl)) {
                        callback.onFail(source.sourceName)
                    } else {
                        callback.onSuccess(source.sourceName, realUrl!!)
                        analysisParseDuration(source.sourceName)
                    }
                    return@subscribe
                }

                val finalHeader = when (firstParseResult.source) {
                    "爱奇艺" -> null
                    "乐视网" -> headerC
                    "QQ" -> headerC
                    "哔哩哔哩" -> header
                    "优酷网" -> header
                    else -> null
                }

                val listener = object : OnFlvcdCallback {
                    override fun onFlvcdSuccess(url: String) {
                        LogC.e("parse -> ${firstParseResult.source} header: ${finalHeader?.keys} 获取到视频地址...$url")
                        callback.onSuccess(source.sourceName, url, finalHeader)
                        analysisParseDuration(source.sourceName)
                    }

                    override fun onFlvcdFail() {
                        callback.onFail(source.sourceName)
                    }
                }

                when (firstParseResult.source) {
                    "爱奇艺" -> parseAqy(firstParseResult, listener)
                    "乐视网" -> parseLE(headerC, firstParseResult, listener)
                    "QQ" -> parseQQ(headerC, firstParseResult, listener)
                    "哔哩哔哩" -> parseBili(headerC, firstParseResult, listener)
                    "优酷网" -> parseYK(targetUrl, header, firstParseResult, listener)
                    else -> callback.onFail(source.sourceName)
                }
            }, {
                callback.onFail(source.sourceName)
            })
    }



    private fun parseAqy(firstParseResult: FLVCDModel, callback: OnFlvcdCallback) {
        if (firstParseResult.v.firstOrNull()?.c == null) {
            callback.onFlvcdFail()
            return
        }
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
            .parseString(firstParseResult.v[0].c)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ keyContent ->
                if (keyContent == null) {
                    LogC.d("parse -> keyContent == null")
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> keyContent -> $keyContent")
                val finalUrl = keyContent.split(firstParseResult.ts).getOrNull(1)?.split(firstParseResult.te)?.getOrNull(0)
                LogC.d("parse -> finalUrl -> $finalUrl")
                if (TextUtils.isEmpty(finalUrl)) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                callback.onFlvcdSuccess(finalUrl!!)
            }, {
                it?.printStackTrace()
                callback.onFlvcdFail()
            })
    }

    private fun parseBili(headerParse: HashMap<String, String>, firstParseResult: FLVCDModel, callback: OnFlvcdCallback) {
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
            .parseString(headerParse, firstParseResult.c)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ keyContent ->
                if (keyContent == null) {
                    LogC.d("parse -> keyContent == null")
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> keyContent -> $keyContent")
                val finalUrl = keyContent.split(firstParseResult.ts).getOrNull(1)?.split(firstParseResult.te)?.getOrNull(0)
                LogC.d("parse -> finalUrl -> $finalUrl")
                if (TextUtils.isEmpty(finalUrl)) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                callback.onFlvcdSuccess(finalUrl!!)
            }, {
                it?.printStackTrace()
                callback.onFlvcdFail()
            })
    }

    private fun parseQQ(header: HashMap<String, String>, firstParseResult: FLVCDModel, callback: OnFlvcdCallback) {
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
            .parseString(header, firstParseResult.c)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ keyContent ->
                if (keyContent == null) {
                    LogC.d("parse -> keyContent == null")
                    callback.onFlvcdFail()
                    return@subscribe
                }
                val vkey = keyContent.split(firstParseResult.ts).getOrNull(1)?.split(firstParseResult.te)?.firstOrNull()
                if (vkey == null) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> vkey -> $vkey")
                var finalUrl: String? = null
                firstParseResult.v?.forEach {
                    finalUrl = it?.u?.replace("vvvkk123", vkey)
                    LogC.d("parse -> finalUrl -> " + it?.u)
                }
                if (TextUtils.isEmpty(finalUrl)) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                callback.onFlvcdSuccess(finalUrl!!)
            }, {
                it?.printStackTrace()
                callback.onFlvcdFail()
            })
    }

    private fun parseLE(header: HashMap<String, String>, firstParseResult: FLVCDModel, callback: OnFlvcdCallback) {
        LogC.d("parse -> cpre -> ${firstParseResult.cpre}")
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
            .parseString(header, firstParseResult.c)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ secondParseResult ->
                if (secondParseResult == null) {
                    LogC.d("parse -> secondParseResult == null")
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> secondParseResult -> $secondParseResult")
                if (!secondParseResult.contains(firstParseResult.cpreTs)) {
                    LogC.d("parse -> 未找到 cpre_ts")
                    callback.onFlvcdFail()
                    return@subscribe
                }
                val cTo = secondParseResult.split(firstParseResult.cpreTs).getOrNull(1)?.split(firstParseResult.cpreTe)?.getOrNull(0)
                    ?.replace("\\/", "/")
                    ?.replace(firstParseResult.cpreFrom, firstParseResult.cpreTo)
                if (cTo == null) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> thirdRequestUrl -> $cTo")
                val thirdRequestUrl = firstParseResult.c?.replace(firstParseResult.cFrom, cTo)
                if (thirdRequestUrl == null) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
                    .parseString(header, firstParseResult.c)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe( stringParse@ { thirdParseResult ->
                        LogC.d("parse -> thirdParseResult -> $thirdParseResult")
                        val cToFinal =
                            thirdParseResult.split(firstParseResult.ts).getOrNull(1)?.split(firstParseResult.te)?.getOrNull(0)?.replace("\\/", "/")
                        LogC.d("parse -> cToFinal -> $cToFinal")
                        if (cToFinal == null) {
                            callback.onFlvcdFail()
                            return@stringParse
                        }
                        callback.onFlvcdSuccess(cToFinal)
                    }, {
                        it?.printStackTrace()
                        callback.onFlvcdFail()
                    })
            }, {
                it?.printStackTrace()
                callback.onFlvcdFail()
            })
    }

    private fun parseYK(targetUrl: String, header: HashMap<String, String>, firstParseResult: FLVCDModel, callback: OnFlvcdCallback) {
        // 取缓存的 utid
        var utid: String? = SPUtil.instance.utid
        val utidCreateTime = SPUtil.instance.utidCreateTime
        val isTimeout = System.currentTimeMillis() - utidCreateTime >= 3 * 60 * 60 * 1000
        if (TextUtils.isEmpty(utid) || isTimeout) { // 需要去请求utid
            // 缓存的 utid 不存在或者 超过3小时，就要重新请求
            disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
                .parseString(firstParseResult.c2)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ requestUtid ->
                    LogC.d("parse -> requestUtid -> $requestUtid")
                    utid = requestUtid?.split(firstParseResult.ts2)?.getOrNull(1)?.split(firstParseResult.te2)?.getOrNull(0)
                    LogC.d("parse -> utid -> $utid")
                    if (TextUtils.isEmpty(utid)) {
                        callback.onFlvcdFail()
                        return@subscribe
                    }
                    LogC.d("parse -> 等待30s，存储utid")
                    disposable = RxUtils.timer(30000) {
                        SPUtil.instance.utid = utid!!
                        SPUtil.instance.utidCreateTime = System.currentTimeMillis()
                        execYKSync(utid!!, targetUrl, header, firstParseResult, callback)
                    }
                }, {
                    callback.onFlvcdFail()
                })
        } else {
            if (TextUtils.isEmpty(utid)) {
                callback.onFlvcdFail()
                return
            }
            execYKSync(utid!!, targetUrl, header, firstParseResult, callback)
        }
    }

    private fun execYKSync(
        utid: String, targetUrl: String, header: HashMap<String, String>, firstParseResult: FLVCDModel, callback:
        OnFlvcdCallback
    ) {
        // 替换c中的utid内容
        val cUri = Uri.parse(firstParseResult.c)
        val utidContent = cUri.getQueryParameter("utid")
        LogC.d("parse -> 替换之前得到的utdid -> $utidContent")
        if (utidContent != null) {
            firstParseResult.c = firstParseResult.c?.replace(utidContent, utid)
        }
        // 携带header，继续请求c，获取最后的keyContent
        disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
            .parseString(header, firstParseResult.c)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ keyContent ->
                if (TextUtils.isEmpty(keyContent)) {
                    callback.onFlvcdFail()
                    return@subscribe
                }
                LogC.d("parse -> keyContent -> $keyContent")
                disposable = RetrofitUtils.instance.getStringApi(AnalysisApi::class.java)
                    .parsePost(header, targetUrl, keyContent!!)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe( postSuccess@ { finalPostRequestJson ->
                        if (finalPostRequestJson == null) {
                            callback.onFlvcdFail()
                            return@postSuccess
                        }
                        try {
                            val finalPostRequest = Gson().fromJson(finalPostRequestJson, FLVCDModel::class.java)
                            LogC.d("parse -> finalPostRequest -> $finalPostRequest")
                            val finalUrl = finalPostRequest?.v?.firstOrNull()?.u
                            if (finalUrl == null) {
                                callback.onFlvcdFail()
                                return@postSuccess
                            }
                            callback.onFlvcdSuccess(finalUrl)
                        } catch (e: Exception) {
                            e.printStackTrace()
                            callback.onFlvcdFail()
                        }
                    }, {
                        callback.onFlvcdFail()
                    })
            }, {
                it?.printStackTrace()
                callback.onFlvcdFail()
            })
    }

    interface OnFlvcdCallback {
        fun onFlvcdSuccess(url: String)
        fun onFlvcdFail()
    }
}